/*-------------------------------------------------------------------------
 *
 * appendinfo.c
 *	  在附加父节点和子节点之间进行映射的例程
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  src/backend/optimizer/util/appendinfo.c
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include "access/htup_details.h"
#include "access/table.h"
#include "foreign/fdwapi.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "optimizer/appendinfo.h"
#include "optimizer/pathnode.h"
#include "optimizer/planmain.h"
#include "parser/parsetree.h"
#include "utils/lsyscache.h"
#include "utils/rel.h"
#include "utils/syscache.h"


typedef struct
{
	PlannerInfo *root;
	int			nappinfos;
	AppendRelInfo **appinfos;
} adjust_appendrel_attrs_context;

static void fc_make_inh_translation_list(Relation fc_oldrelation,
									  Relation fc_newrelation,
									  Index fc_newvarno,
									  AppendRelInfo *fc_appinfo);
static Node *fc_adjust_appendrel_attrs_mutator(Node *fc_node,
											adjust_appendrel_attrs_context *fc_context);


/*
 * make_append_rel_info
 *	  为父子对构建AppendRelInfo
 */
AppendRelInfo *
make_append_rel_info(Relation fc_parentrel, Relation fc_childrel,
					 Index fc_parentRTindex, Index fc_childRTindex)
{
	AppendRelInfo *fc_appinfo = makeNode(AppendRelInfo);

	fc_appinfo->parent_relid = fc_parentRTindex;
	fc_appinfo->child_relid = fc_childRTindex;
	fc_appinfo->parent_reltype = fc_parentrel->rd_rel->reltype;
	fc_appinfo->child_reltype = fc_childrel->rd_rel->reltype;
	fc_make_inh_translation_list(fc_parentrel, fc_childrel, fc_childRTindex, fc_appinfo);
	fc_appinfo->parent_reloid = RelationGetRelid(fc_parentrel);

	return fc_appinfo;
}


/*
 * make_inh_translation_list
 *	  构建从父变量到子变量的翻译列表，用于继承子，
 *	  以及一个反向翻译数组。
 *
 * 反向翻译数组为每个子关系列提供一个条目，
 * 该条目是相应父列的从1开始的索引，或者如果没有匹配则是0（这发生在删除的子列上，
 * 以及超出父列的子列，传统继承中是允许的，但在分区中不允许）。
 *
 * 为了谨慎起见，我们匹配类型/排序规则以及属性名称。
 */
static void fc_make_inh_translation_list(Relation fc_oldrelation, Relation fc_newrelation,
						  Index fc_newvarno,
						  AppendRelInfo *fc_appinfo)
{
	List	   *fc_vars = NIL;
	AttrNumber *fc_pcolnos;
	TupleDesc	fc_old_tupdesc = RelationGetDescr(fc_oldrelation);
	TupleDesc	fc_new_tupdesc = RelationGetDescr(fc_newrelation);
	Oid			fc_new_relid = RelationGetRelid(fc_newrelation);
	int			fc_oldnatts = fc_old_tupdesc->natts;
	int			fc_newnatts = fc_new_tupdesc->natts;
	int			fc_old_attno;
	int			fc_new_attno = 0;

	/* 使用所有条目为零初始化反向翻译数组 */
	fc_appinfo->num_child_cols = fc_newnatts;
	fc_appinfo->parent_colnos = fc_pcolnos =
		(AttrNumber *) palloc0(fc_newnatts * sizeof(AttrNumber));

	for (fc_old_attno = 0; fc_old_attno < fc_oldnatts; fc_old_attno++)
	{
		Form_pg_attribute fc_att;
		char	   *fc_attname;
		Oid			fc_atttypid;
		int32		fc_atttypmod;
		Oid			fc_attcollation;

		fc_att = TupleDescAttr(fc_old_tupdesc, fc_old_attno);
		if (fc_att->attisdropped)
		{
			/* 只需将NULL放入此列表条目 */
			fc_vars = lappend(fc_vars, NULL);
			continue;
		}
		fc_attname = NameStr(fc_att->attname);
		fc_atttypid = fc_att->atttypid;
		fc_atttypmod = fc_att->atttypmod;
		fc_attcollation = fc_att->attcollation;

		/*
		 * 当我们为继承集合的父表生成“翻译列表”时，
		 * 不需要搜索匹配项。
		 */
		if (fc_oldrelation == fc_newrelation)
		{
			fc_vars = lappend(fc_vars, makeVar(fc_newvarno,
										 (AttrNumber) (fc_old_attno + 1),
										 fc_atttypid,
										 fc_atttypmod,
										 fc_attcollation,
										 0));
			fc_pcolnos[fc_old_attno] = fc_old_attno + 1;
			continue;
		}

		/*
		 * 否则，我们必须通过名称搜索匹配列。
		 * 由于像ALTER TABLE ADD COLUMN和多重继承这样的情况，
		 * 没有保证它会具有相同的列位置。
		 * 但是，在简单的情况下，两个关系的列的相对顺序大部分时间都是相同的，
		 * 所以尝试新的关系中紧跟在我们刚刚找到的列后面的列，如果失败，
		 * 让系统缓存来处理。
		 */
		if (fc_new_attno >= fc_newnatts ||
			(fc_att = TupleDescAttr(fc_new_tupdesc, fc_new_attno))->attisdropped ||
			strcmp(fc_attname, NameStr(fc_att->attname)) != 0)
		{
			HeapTuple	fc_newtup;

			fc_newtup = SearchSysCacheAttName(fc_new_relid, fc_attname);
			if (!HeapTupleIsValid(fc_newtup))
				elog(ERROR, "could not find inherited attribute \"%s\" of relation \"%s\"",
					 fc_attname, RelationGetRelationName(fc_newrelation));
			fc_new_attno = ((Form_pg_attribute) GETSTRUCT(fc_newtup))->attnum - 1;
			Assert(fc_new_attno >= 0 && fc_new_attno < fc_newnatts);
			ReleaseSysCache(fc_newtup);

			fc_att = TupleDescAttr(fc_new_tupdesc, fc_new_attno);
		}

		/* 找到了，检查类型和排序规则是否匹配 */
		if (fc_atttypid != fc_att->atttypid || fc_atttypmod != fc_att->atttypmod)
			elog(ERROR, "attribute \"%s\" of relation \"%s\" does not match parent's type",
				 fc_attname, RelationGetRelationName(fc_newrelation));
		if (fc_attcollation != fc_att->attcollation)
			elog(ERROR, "attribute \"%s\" of relation \"%s\" does not match parent's collation",
				 fc_attname, RelationGetRelationName(fc_newrelation));

		fc_vars = lappend(fc_vars, makeVar(fc_newvarno,
									 (AttrNumber) (fc_new_attno + 1),
									 fc_atttypid,
									 fc_atttypmod,
									 fc_attcollation,
									 0));
		fc_pcolnos[fc_new_attno] = fc_old_attno + 1;
		fc_new_attno++;
	}

	fc_appinfo->translated_vars = fc_vars;
}

/*
 * adjust_appendrel_attrs
 *	  复制指定的查询或表达式，并将引用父关系的变量转换为
 *	  相应子关系。我们还更新出现在变量外部的rtindexes，
 *	  例如resultRelation和jointree relids。
 *
 * 注意：这仅在将子链接转换为子计划后应用，
 * 因此我们不需要处理递归到子查询中。
 *
 * 注意：这与pullup_replace_vars()所做的没有太大不同；
 * 也许我们应该尝试将两者合并在一起。
 */
Node * adjust_appendrel_attrs(PlannerInfo *fc_root, Node *fc_node, int fc_nappinfos,
					   AppendRelInfo **fc_appinfos)
{
	adjust_appendrel_attrs_context fc_context;

	fc_context.root = fc_root;
	fc_context.nappinfos = fc_nappinfos;
	fc_context.appinfos = fc_appinfos;

	/* 如果没有需要调整的内容，别调用这个函数。 */
	Assert(fc_nappinfos >= 1 && fc_appinfos != NULL);

	/* 不应该翻译查询树。 */
	Assert(fc_node == NULL || !IsA(fc_node, Query));

	return fc_adjust_appendrel_attrs_mutator(fc_node, &fc_context);
}

static Node * fc_adjust_appendrel_attrs_mutator(Node *fc_node,
							   adjust_appendrel_attrs_context *fc_context)
{
	AppendRelInfo **fc_appinfos = fc_context->appinfos;
	int			fc_nappinfos = fc_context->nappinfos;
	int			fc_cnt;

	if (fc_node == NULL)
		return NULL;
	if (IsA(fc_node, Var))
	{
		Var		   *fc_var = (Var *) copyObject(fc_node);
		AppendRelInfo *fc_appinfo = NULL;

		if (fc_var->varlevelsup != 0)
			return (Node *) fc_var;	/* 无需更改 */

		for (fc_cnt = 0; fc_cnt < fc_nappinfos; fc_cnt++)
		{
			if (fc_var->varno == fc_appinfos[fc_cnt]->parent_relid)
			{
				fc_appinfo = fc_appinfos[fc_cnt];
				break;
			}
		}

		if (fc_appinfo)
		{
			fc_var->varno = fc_appinfo->child_relid;
			/* 现在它是一个生成的 Var，因此删除任何语法标记 */
			fc_var->varnosyn = 0;
			fc_var->varattnosyn = 0;
			if (fc_var->varattno > 0)
			{
				Node	   *fc_newnode;

				if (fc_var->varattno > list_length(fc_appinfo->translated_vars))
					elog(ERROR, "attribute %d of relation \"%s\" does not exist",
						 fc_var->varattno, get_rel_name(fc_appinfo->parent_reloid));
				fc_newnode = copyObject(list_nth(fc_appinfo->translated_vars,
											  fc_var->varattno - 1));
				if (fc_newnode == NULL)
					elog(ERROR, "attribute %d of relation \"%s\" does not exist",
						 fc_var->varattno, get_rel_name(fc_appinfo->parent_reloid));
				return fc_newnode;
			}
			else if (fc_var->varattno == 0)
			{
				/*
				 * 整行 Var：如果我们处理的是命名行类型，
				 * 我们可以为子表使用整行 Var，并进行强制转换步骤
				 * 将元组布局转换为父的行类型。
				 * 否则，我们必须生成一个 RowExpr。
				 */
				if (OidIsValid(fc_appinfo->child_reltype))
				{
					Assert(fc_var->vartype == fc_appinfo->parent_reltype);
					if (fc_appinfo->parent_reltype != fc_appinfo->child_reltype)
					{
						ConvertRowtypeExpr *fc_r = makeNode(ConvertRowtypeExpr);

						fc_r->arg = (Expr *) fc_var;
						fc_r->resulttype = fc_appinfo->parent_reltype;
						fc_r->convertformat = COERCE_IMPLICIT_CAST;
						fc_r->location = -1;
						/* 确保 Var 节点也具有正确的类型 ID */
						fc_var->vartype = fc_appinfo->child_reltype;
						return (Node *) fc_r;
					}
				}
				else
				{
					/*
					 * 构建一个包含翻译变量的 RowExpr。
					 *
					 * 在实际中，var->vartype 在这里将始终是 RECORDOID，
					 * 因此我们需要提出一些合适的列名。
					 * 我们使用父 RTE 的列名。
					 *
					 * 注意：我们不能在继承情况下到达这里，因此没有
					 * 需要担心 translated_vars 可能包含一些虚拟 NULL。
					 */
					RowExpr    *fc_rowexpr;
					List	   *fc_fields;
					RangeTblEntry *fc_rte;

					fc_rte = rt_fetch(fc_appinfo->parent_relid,
								   fc_context->root->parse->rtable);
					fc_fields = copyObject(fc_appinfo->translated_vars);
					fc_rowexpr = makeNode(RowExpr);
					fc_rowexpr->args = fc_fields;
					fc_rowexpr->row_typeid = fc_var->vartype;
					fc_rowexpr->row_format = COERCE_IMPLICIT_CAST;
					fc_rowexpr->colnames = copyObject(fc_rte->eref->colnames);
					fc_rowexpr->location = -1;

					return (Node *) fc_rowexpr;
				}
			}
			/* 系统属性不需要任何其他翻译 */
		}
		else if (fc_var->varno == ROWID_VAR)
		{
			/*
			 * 如果这是一个 ROWID_VAR 占位符，看看我们是否已到达一个叶子
			 * 目标关系，我们可以将 Var 翻译为特定的
			 * 实例化。我们不应该被要求翻译到包含多个叶子
			 * 目标关系的 relid 集，因此答案将是唯一的。
			 * 如果我们仍在考虑非叶子
			 * 继承级别，则原样返回 ROWID_VAR Var。
			 */
			Relids		fc_leaf_result_relids = fc_context->root->leaf_result_relids;
			Index		fc_leaf_relid = 0;

			for (fc_cnt = 0; fc_cnt < fc_nappinfos; fc_cnt++)
			{
				if (bms_is_member(fc_appinfos[fc_cnt]->child_relid,
								  fc_leaf_result_relids))
				{
					if (fc_leaf_relid)
						elog(ERROR, "cannot translate to multiple leaf relids");
					fc_leaf_relid = fc_appinfos[fc_cnt]->child_relid;
				}
			}

			if (fc_leaf_relid)
			{
				RowIdentityVarInfo *fc_ridinfo = (RowIdentityVarInfo *)
				list_nth(fc_context->root->row_identity_vars, fc_var->varattno - 1);

				if (bms_is_member(fc_leaf_relid, fc_ridinfo->rowidrels))
				{
					/* 替代 RowIdentityVarInfo 中给定的 Var */
					fc_var = copyObject(fc_ridinfo->rowidvar);
					/* ... 但使用正确的 relid */
					fc_var->varno = fc_leaf_relid;
					/* RowIdentityVarInfo 中的 varnosyn 可能是错误的 */
					fc_var->varnosyn = 0;
					fc_var->varattnosyn = 0;
				}
				else
				{
					/*
					 * 这个叶子关系无法返回所需的值，因此
					 * 替代一个正确类型的 NULL。
					 */
					return (Node *) makeNullConst(fc_var->vartype,
												  fc_var->vartypmod,
												  fc_var->varcollid);
				}
			}
		}
		return (Node *) fc_var;
	}
	if (IsA(fc_node, CurrentOfExpr))
	{
		CurrentOfExpr *fc_cexpr = (CurrentOfExpr *) copyObject(fc_node);

		for (fc_cnt = 0; fc_cnt < fc_nappinfos; fc_cnt++)
		{
			AppendRelInfo *fc_appinfo = fc_appinfos[fc_cnt];

			if (fc_cexpr->cvarno == fc_appinfo->parent_relid)
			{
				fc_cexpr->cvarno = fc_appinfo->child_relid;
				break;
			}
		}
		return (Node *) fc_cexpr;
	}
	if (IsA(fc_node, PlaceHolderVar))
	{
		/* 复制 PlaceHolderVar 节点并正确修改子节点 */
		PlaceHolderVar *fc_phv;

		fc_phv = (PlaceHolderVar *) expression_tree_mutator(fc_node,
														 fc_adjust_appendrel_attrs_mutator,
														 (void *) fc_context);
		/* 现在修复 PlaceHolderVar 的 relid 集 */
		if (fc_phv->phlevelsup == 0)
			fc_phv->phrels = adjust_child_relids(fc_phv->phrels, fc_context->nappinfos,
											  fc_context->appinfos);
		return (Node *) fc_phv;
	}
	/* 这里不需要处理规划辅助节点 */
	Assert(!IsA(fc_node, SpecialJoinInfo));
	Assert(!IsA(fc_node, AppendRelInfo));
	Assert(!IsA(fc_node, PlaceHolderInfo));
	Assert(!IsA(fc_node, MinMaxAggInfo));

	/*
	 * 我们必须特别处理 RestrictInfo 节点。 （注意：虽然
	 * set_append_rel_pathlist 会隐藏父级的 RestrictInfos
	 * baserestrictinfo 列表，但它不会隐藏连接信息中的那些。）
	 */
	if (IsA(fc_node, RestrictInfo))
	{
		RestrictInfo *fc_oldinfo = (RestrictInfo *) fc_node;
		RestrictInfo *fc_newinfo = makeNode(RestrictInfo);

		/* 复制所有可扁平化的字段 */
		memcpy(fc_newinfo, fc_oldinfo, sizeof(RestrictInfo));

		/* 递归修复子句本身 */
		fc_newinfo->clause = (Expr *)
			fc_adjust_appendrel_attrs_mutator((Node *) fc_oldinfo->clause, fc_context);

		/* 如果是 OR 子句，也修复修改后的版本 */
		fc_newinfo->orclause = (Expr *)
			fc_adjust_appendrel_attrs_mutator((Node *) fc_oldinfo->orclause, fc_context);

		/* 也调整 relid 集 */
		fc_newinfo->clause_relids = adjust_child_relids(fc_oldinfo->clause_relids,
													 fc_context->nappinfos,
													 fc_context->appinfos);
		fc_newinfo->required_relids = adjust_child_relids(fc_oldinfo->required_relids,
													   fc_context->nappinfos,
													   fc_context->appinfos);
		fc_newinfo->outer_relids = adjust_child_relids(fc_oldinfo->outer_relids,
													fc_context->nappinfos,
													fc_context->appinfos);
		fc_newinfo->nullable_relids = adjust_child_relids(fc_oldinfo->nullable_relids,
													   fc_context->nappinfos,
													   fc_context->appinfos);
		fc_newinfo->left_relids = adjust_child_relids(fc_oldinfo->left_relids,
												   fc_context->nappinfos,
												   fc_context->appinfos);
		fc_newinfo->right_relids = adjust_child_relids(fc_oldinfo->right_relids,
													fc_context->nappinfos,
													fc_context->appinfos);

		/*
		 * 重置缓存的派生字段，因为在考虑子关系时这些字段可能需要
		 * 不同的值。请注意，我们不重置 left_ec/right_ec：每个子变量隐含地
		 * 等同于其父级，因此如果存在，它仍然是同一 EC 的成员。
		 */
		fc_newinfo->eval_cost.startup = -1;
		fc_newinfo->norm_selec = -1;
		fc_newinfo->outer_selec = -1;
		fc_newinfo->left_em = NULL;
		fc_newinfo->right_em = NULL;
		fc_newinfo->scansel_cache = NIL;
		fc_newinfo->left_bucketsize = -1;
		fc_newinfo->right_bucketsize = -1;
		fc_newinfo->left_mcvfreq = -1;
		fc_newinfo->right_mcvfreq = -1;

		return (Node *) fc_newinfo;
	}

	/*
	 * 注意：我们不需要递归进入子链接，因为它们应该
	 * 在我们看到它们之前已经转换为子计划。
	 */
	Assert(!IsA(fc_node, SubLink));
	Assert(!IsA(fc_node, Query));
	/* 我们也不应该看到这些查询子结构。 */
	Assert(!IsA(fc_node, RangeTblRef));
	Assert(!IsA(fc_node, JoinExpr));

	return expression_tree_mutator(fc_node, fc_adjust_appendrel_attrs_mutator,
								   (void *) fc_context);
}

/*
 * adjust_appendrel_attrs_multilevel
 *	  将 Var 转换从顶层 appendrel 父级应用到子级。
 *
 * 在某些情况下，我们需要将引用父关系的表达式翻译为
 * 引用与之多级隔离的 appendrel 子级。
 */
Node * adjust_appendrel_attrs_multilevel(PlannerInfo *fc_root, Node *fc_node,
								  Relids fc_child_relids,
								  Relids fc_top_parent_relids)
{
	AppendRelInfo **fc_appinfos;
	Bitmapset  *fc_parent_relids = NULL;
	int			fc_nappinfos;
	int			fc_cnt;

	Assert(bms_num_members(fc_child_relids) == bms_num_members(fc_top_parent_relids));

	fc_appinfos = find_appinfos_by_relids(fc_root, fc_child_relids, &fc_nappinfos);

	/* 为给定子级构造直接父级的 relid 集。 */
	for (fc_cnt = 0; fc_cnt < fc_nappinfos; fc_cnt++)
	{
		AppendRelInfo *fc_appinfo = fc_appinfos[fc_cnt];

		fc_parent_relids = bms_add_member(fc_parent_relids, fc_appinfo->parent_relid);
	}

	/* 如果直接父级不是顶级父级，则递归。 */
	if (!bms_equal(fc_parent_relids, fc_top_parent_relids))
		fc_node = adjust_appendrel_attrs_multilevel(fc_root, fc_node, fc_parent_relids,
												 fc_top_parent_relids);

	/* 现在为这个子级翻译 */
	fc_node = adjust_appendrel_attrs(fc_root, fc_node, fc_nappinfos, fc_appinfos);

	pfree(fc_appinfos);

	return fc_node;
}

/*
 * 在 Relid 集中用子级 relid 替换父级 relid。appinfos 的数组
 * 指定要执行的替换。
 */
Relids adjust_child_relids(Relids fc_relids, int fc_nappinfos, AppendRelInfo **fc_appinfos)
{
	Bitmapset  *fc_result = NULL;
	int			fc_cnt;

	for (fc_cnt = 0; fc_cnt < fc_nappinfos; fc_cnt++)
	{
		AppendRelInfo *fc_appinfo = fc_appinfos[fc_cnt];

		/* 删除父级，添加子级 */
		if (bms_is_member(fc_appinfo->parent_relid, fc_relids))
		{
			/* 如果我们正在更改集合，则进行复制。 */
			if (!fc_result)
				fc_result = bms_copy(fc_relids);

			fc_result = bms_del_member(fc_result, fc_appinfo->parent_relid);
			fc_result = bms_add_member(fc_result, fc_appinfo->child_relid);
		}
	}

	/* 如果我们进行了任何更改，返回修改后的副本。 */
	if (fc_result)
		return fc_result;

	/* 否则，返回未修改的原始集合。 */
	return fc_relids;
}

/*
 * 用子关系中的子项替换顶层父关系中的任何 relid。
 * child_relids 中的成员可以在分区层次结构中位于顶层父关系以下多个级别。
 */
Relids adjust_child_relids_multilevel(PlannerInfo *fc_root, Relids fc_relids,
							   Relids fc_child_relids, Relids fc_top_parent_relids)
{
	AppendRelInfo **fc_appinfos;
	int			fc_nappinfos;
	Relids		fc_parent_relids = NULL;
	Relids		fc_result;
	Relids		fc_tmp_result = NULL;
	int			fc_cnt;

	/*
	 * 如果给定的 relids 集中不包含任何顶层父关系的 relids，
	 * 它将保持不变。
	 */
	if (!bms_overlap(fc_relids, fc_top_parent_relids))
		return fc_relids;

	fc_appinfos = find_appinfos_by_relids(fc_root, fc_child_relids, &fc_nappinfos);

	/* 为给定子项的直接父项构造 relids 集。 */
	for (fc_cnt = 0; fc_cnt < fc_nappinfos; fc_cnt++)
	{
		AppendRelInfo *fc_appinfo = fc_appinfos[fc_cnt];

		fc_parent_relids = bms_add_member(fc_parent_relids, fc_appinfo->parent_relid);
	}

	/* 如果直接父级不是顶级父级，则递归。 */
	if (!bms_equal(fc_parent_relids, fc_top_parent_relids))
	{
		fc_tmp_result = adjust_child_relids_multilevel(fc_root, fc_relids,
													fc_parent_relids,
													fc_top_parent_relids);
		fc_relids = fc_tmp_result;
	}

	fc_result = adjust_child_relids(fc_relids, fc_nappinfos, fc_appinfos);

	/* 释放任何中间结果占用的内存。 */
	if (fc_tmp_result)
		bms_free(fc_tmp_result);
	bms_free(fc_parent_relids);
	pfree(fc_appinfos);

	return fc_result;
}

/*
 * adjust_inherited_attnums
 *	  将父项的属性编号整数列表转换为子项的属性编号。
 */
List * adjust_inherited_attnums(List *fc_attnums, AppendRelInfo *fc_context)
{
	List	   *fc_result = NIL;
	ListCell   *fc_lc;

	/* 这只应发生在继承情况下，而不是 UNION ALL */
	Assert(OidIsValid(fc_context->parent_reloid));

	/* 在 AppendRelInfo 的 translated_vars 列表中查找每个属性 */
	foreach(fc_lc, fc_attnums)
	{
		AttrNumber	fc_parentattno = lfirst_int(fc_lc);
		Var		   *fc_childvar;

		/* 查找该列的转换：它必须是 Var */
		if (fc_parentattno <= 0 ||
			fc_parentattno > list_length(fc_context->translated_vars))
			elog(ERROR, "attribute %d of relation \"%s\" does not exist",
				 fc_parentattno, get_rel_name(fc_context->parent_reloid));
		fc_childvar = (Var *) list_nth(fc_context->translated_vars, fc_parentattno - 1);
		if (fc_childvar == NULL || !IsA(fc_childvar, Var))
			elog(ERROR, "attribute %d of relation \"%s\" does not exist",
				 fc_parentattno, get_rel_name(fc_context->parent_reloid));

		fc_result = lappend_int(fc_result, fc_childvar->varattno);
	}
	return fc_result;
}

/*
 * adjust_inherited_attnums_multilevel
 *	  如上所述，但根据需要遍历多个继承级别。
 */
List * adjust_inherited_attnums_multilevel(PlannerInfo *fc_root, List *fc_attnums,
									Index fc_child_relid, Index fc_top_parent_relid)
{
	AppendRelInfo *fc_appinfo = fc_root->append_rel_array[fc_child_relid];

	if (!fc_appinfo)
		elog(ERROR, "child rel %d not found in append_rel_array", fc_child_relid);

	/* 如果直接父级不是顶级父级，则递归。 */
	if (fc_appinfo->parent_relid != fc_top_parent_relid)
		fc_attnums = adjust_inherited_attnums_multilevel(fc_root, fc_attnums,
													  fc_appinfo->parent_relid,
													  fc_top_parent_relid);

	/* 现在为这个子级翻译 */
	return adjust_inherited_attnums(fc_attnums, fc_appinfo);
}

/*
 * get_translated_update_targetlist
 *	  获取 UPDATE 查询的 processed_tlist，根据需要转换以
 *	  匹配子目标关系。
 *
 * 可选择还返回目标列编号的列表，转换为此目标关系。 
 * （处理的 tlist 中的 resnos 不应被依赖于此目的。）
 */
void get_translated_update_targetlist(PlannerInfo *fc_root, Index fc_relid,
								 List **fc_processed_tlist, List **update_colnos)
{
	/* 对于除 UPDATE 之外的命令，这毫无意义。 */
	Assert(fc_root->parse->commandType == CMD_UPDATE);
	if (fc_relid == fc_root->parse->resultRelation)
	{
		/*
		 * 非继承情况，因此很简单。调用者可能希望
		 * 有一个可以随意修改的树，因此进行复制。
		 */
		*fc_processed_tlist = copyObject(fc_root->processed_tlist);
		if (update_colnos)
			*update_colnos = copyObject(fc_root->update_colnos);
	}
	else
	{
		Assert(bms_is_member(fc_relid, fc_root->all_result_relids));
		*fc_processed_tlist = (List *)
			adjust_appendrel_attrs_multilevel(fc_root,
											  (Node *) fc_root->processed_tlist,
											  bms_make_singleton(fc_relid),
											  bms_make_singleton(fc_root->parse->resultRelation));
		if (update_colnos)
			*update_colnos =
				adjust_inherited_attnums_multilevel(fc_root, fc_root->update_colnos,
													fc_relid,
													fc_root->parse->resultRelation);
	}
}

/*
 * find_appinfos_by_relids
 * 		查找所有由 relids 指定的关系的 AppendRelInfo 结构。
 *
 * AppendRelInfos 以数组的形式返回，调用者可以 pfree。
 * *nappinfos 设置为数组中的条目数量。
 */
AppendRelInfo **
find_appinfos_by_relids(PlannerInfo *fc_root, Relids fc_relids, int *fc_nappinfos)
{
	AppendRelInfo **fc_appinfos;
	int			fc_cnt = 0;
	int			fc_i;

	*fc_nappinfos = bms_num_members(fc_relids);
	fc_appinfos = (AppendRelInfo **) palloc(sizeof(AppendRelInfo *) * *fc_nappinfos);

	fc_i = -1;
	while ((fc_i = bms_next_member(fc_relids, fc_i)) >= 0)
	{
		AppendRelInfo *fc_appinfo = fc_root->append_rel_array[fc_i];

		if (!fc_appinfo)
			elog(ERROR, "child rel %d not found in append_rel_array", fc_i);

		fc_appinfos[fc_cnt++] = fc_appinfo;
	}
	return fc_appinfos;
}


/*****************************************************************************
 *
 *		ROW-IDENTITY VARIABLE MANAGEMENT
 *
 * 这段代码可能缺乏一个好的组织，我们选择将其放在这里，
 * 因为 adjust_appendrel_attrs_mutator() 是其主要合作函数。 
 * 该函数完成了将 ROWID_VAR Vars 扩展为正确内容所需的大部分工作。
 *
 *****************************************************************************/

/*
 * add_row_identity_var
 *	  注册用于 UPDATE/DELETE/MERGE 的行标识列。
 *
 * Var 除了 varno 之外，必须与任何具有相同 rowid_name 的其他行标识列相等。
 * 因此，例如，“wholerow”行 ID 最好使用 vartype == RECORDOID。
 *
 * rtindex 当前与 rowid_var->varno 冗余，但我们将其指定为单独的参数，
 * 以防将来某个时候将其通用化以支持非 Var 表达式。
 * （我们可以合理地处理指定 rtindex 的 Vars 上的表达式，但就现在而言似乎不必要。）
 */
void add_row_identity_var(PlannerInfo *fc_root, Var *fc_orig_var,
					 Index fc_rtindex, const char *fc_rowid_name)
{
	TargetEntry *fc_tle;
	Var		   *fc_rowid_var;
	RowIdentityVarInfo *fc_ridinfo;
	ListCell   *fc_lc;

	/* 目前，参数必须仅是给定 rtindex 的 Var */
	Assert(IsA(fc_orig_var, Var));
	Assert(fc_orig_var->varno == fc_rtindex);
	Assert(fc_orig_var->varlevelsup == 0);

	/*
	 * 如果我们正在进行非继承的 UPDATE/DELETE/MERGE，则几乎不需要
	 * 进行 ROWID_VAR 的杂耍。只需将呈现的 Var 放入
	 * processed_tlist 中，我们就完成了。
	 */
	if (fc_rtindex == fc_root->parse->resultRelation)
	{
		fc_tle = makeTargetEntry((Expr *) fc_orig_var,
							  list_length(fc_root->processed_tlist) + 1,
							  pstrdup(fc_rowid_name),
							  true);
		fc_root->processed_tlist = lappend(fc_root->processed_tlist, fc_tle);
		return;
	}

	/*
	 * 否则，rtindex 应该引用一个在 expand_inherited_rtentry() 
	 * 中添加到查询的叶目标关系。
	 */
	Assert(bms_is_member(fc_rtindex, fc_root->leaf_result_relids));
	Assert(fc_root->append_rel_array[fc_rtindex] != NULL);

	/*
	 * 我们必须找到一个匹配的 RowIdentityVarInfo，或者如果没有，则创建一个。
	 * 为了允许使用 equal() 来匹配变量，将 varno 更改为 ROWID_VAR，
	 * 其他不变。
	 */
	fc_rowid_var = copyObject(fc_orig_var);
	/* 这最终可能成为 ChangeVarNodes() */
	fc_rowid_var->varno = ROWID_VAR;

	/* 查找同名的现有行标识列 */
	foreach(fc_lc, fc_root->row_identity_vars)
	{
		fc_ridinfo = (RowIdentityVarInfo *) lfirst(fc_lc);
		if (strcmp(fc_rowid_name, fc_ridinfo->rowidname) != 0)
			continue;
		if (equal(fc_rowid_var, fc_ridinfo->rowidvar))
		{
			/* 找到了匹配项；我们只需要记录 rtindex 也需要它 */
			fc_ridinfo->rowidrels = bms_add_member(fc_ridinfo->rowidrels, fc_rtindex);
			return;
		}
		else
		{
			/* 哎呀，无法处理此项 */
			elog(ERROR, "conflicting uses of row-identity name \"%s\"",
				 fc_rowid_name);
		}
	}

	/* 尚未请求，因此添加一个新的 RowIdentityVarInfo */
	fc_ridinfo = makeNode(RowIdentityVarInfo);
	fc_ridinfo->rowidvar = copyObject(fc_rowid_var);
	/* 目前，使用数据类型信息估算宽度 */
	fc_ridinfo->rowidwidth = get_typavgwidth(exprType((Node *) fc_rowid_var),
										  exprTypmod((Node *) fc_rowid_var));
	fc_ridinfo->rowidname = pstrdup(fc_rowid_name);
	fc_ridinfo->rowidrels = bms_make_singleton(fc_rtindex);

	fc_root->row_identity_vars = lappend(fc_root->row_identity_vars, fc_ridinfo);

	/* 将 rowid_var 更改为对该 row_identity_vars 条目的引用 */
	fc_rowid_var->varattno = list_length(fc_root->row_identity_vars);

	/* 将 ROWID_VAR 引用变量推入 processed_tlist */
	fc_tle = makeTargetEntry((Expr *) fc_rowid_var,
						  list_length(fc_root->processed_tlist) + 1,
						  pstrdup(fc_rowid_name),
						  true);
	fc_root->processed_tlist = lappend(fc_root->processed_tlist, fc_tle);
}

/*
 * add_row_identity_columns
 *
 * 此函数添加核心代码所需的行标识列。
 * FDWs 可能会自己调用 add_row_identity_var() 来添加非标准列。
 * （重复请求是可以的。）
 */
void add_row_identity_columns(PlannerInfo *fc_root, Index fc_rtindex,
						 RangeTblEntry *fc_target_rte,
						 Relation fc_target_relation)
{
	CmdType		fc_commandType = fc_root->parse->commandType;
	char		fc_relkind = fc_target_relation->rd_rel->relkind;
	Var		   *fc_var;

	Assert(fc_commandType == CMD_UPDATE || fc_commandType == CMD_DELETE || fc_commandType == CMD_MERGE);

	if (fc_commandType == CMD_MERGE ||
		fc_relkind == RELKIND_RELATION ||
		fc_relkind == RELKIND_MATVIEW ||
		fc_relkind == RELKIND_PARTITIONED_TABLE)
	{
		/*
		 * 发出 CTID，以便执行器可以找到要合并、更新或
		 * 删除的行。
		 */
		fc_var = makeVar(fc_rtindex,
					  SelfItemPointerAttributeNumber,
					  TIDOID,
					  -1,
					  InvalidOid,
					  0);
		add_row_identity_var(fc_root, fc_var, fc_rtindex, "ctid");
	}
	else if (fc_relkind == RELKIND_FOREIGN_TABLE)
	{
		/*
		 * 让外部表的 FDW 添加任何它想要的 TLE。
		 */
		FdwRoutine *fc_fdwroutine;

		fc_fdwroutine = GetFdwRoutineForRelation(fc_target_relation, false);

		if (fc_fdwroutine->AddForeignUpdateTargets != NULL)
			fc_fdwroutine->AddForeignUpdateTargets(fc_root, fc_rtindex,
												fc_target_rte, fc_target_relation);

		/*
		 * 对于 UPDATE，我们需要通过请求整个行的 Var 来使 FDW 获取未更改的列。
		 * 这是因为顶层目标列表只包含已更改列的条目，但 ExecUpdate 将需要构建完整的新元组。
		 * （实际上，我们在未推送到远程端的 UPDATE 中才真正需要这个，但在调用此函数时很难判断是否会出现这种情况。）
		 *
		 * 如果有任何行触发器，我们也将需要整个行，以便执行器可以将“旧”行传递给触发器。
		 * 可惜，这样会遗漏系统列。
		 */
		if (fc_commandType == CMD_UPDATE ||
			(fc_target_relation->trigdesc &&
			 (fc_target_relation->trigdesc->trig_delete_after_row ||
			  fc_target_relation->trigdesc->trig_delete_before_row)))
		{
			fc_var = makeVar(fc_rtindex,
						  InvalidAttrNumber,
						  RECORDOID,
						  -1,
						  InvalidOid,
						  0);
			add_row_identity_var(fc_root, fc_var, fc_rtindex, "wholerow");
		}
	}
}

/*
 * distribute_row_identity_vars
 *
 * 在我们识别完继承的 UPDATE/DELETE/MERGE 查询所需的所有行身份列后，
 * 确保这些列会由所有目标关系生成。
 *
 * 这或多或少与 build_base_rel_tlists() 的工作类似，
 * 只不过它不会理解如何处理 ROWID_VAR Vars。
 * 由于该函数在继承关系展开之前运行，因此它根本不会看到任何这种 Vars。
 */
void distribute_row_identity_vars(PlannerInfo *fc_root)
{
	Query	   *fc_parse = fc_root->parse;
	int			fc_result_relation = fc_parse->resultRelation;
	RangeTblEntry *fc_target_rte;
	RelOptInfo *fc_target_rel;
	ListCell   *fc_lc;

	/*
	 * 如果这不是一个继承的 UPDATE/DELETE/MERGE，则无需进行任何操作。
	 */
	if (fc_parse->commandType != CMD_UPDATE && fc_parse->commandType != CMD_DELETE &&
		fc_parse->commandType != CMD_MERGE)
	{
		Assert(fc_root->row_identity_vars == NIL);
		return;
	}
	fc_target_rte = rt_fetch(fc_result_relation, fc_parse->rtable);
	if (!fc_target_rte->inh)
	{
		Assert(fc_root->row_identity_vars == NIL);
		return;
	}

	/*
	 * 通常，我们期望叶子结果关系会将一些 ROWID_VAR Vars 添加到查询中。
	 * 然而，约束排除可能抑制了每个叶子关系。
	 * 如果计划根本没有行身份列，执行器会感到不满，尽管它肯定不会处理任何行。
	 * 通过重新打开顶层结果关系并添加它会使用的行身份列来处理这个边缘情况，
	 * 就像 preprocess_targetlist() 如果没有标记为“inh”时所做的那样。
	 * 然后重新运行 build_base_rel_tlists() 以确保添加的列被传播到关系的 reltarget。
	 * （这有点丑陋，但似乎将丑陋和额外周期限制在这个不寻常的边缘情况下更好。）
	 */
	if (fc_root->row_identity_vars == NIL)
	{
		Relation	fc_target_relation;

		fc_target_relation = table_open(fc_target_rte->relid, NoLock);
		add_row_identity_columns(fc_root, fc_result_relation,
								 fc_target_rte, fc_target_relation);
		table_close(fc_target_relation, NoLock);
		build_base_rel_tlists(fc_root, fc_root->processed_tlist);
		/* 在这种情况下没有 ROWID_VAR Vars，所以我们完成了。 */
		return;
	}

	/*
	 * 在 processed_tlist 中查找 ROWID_VAR 引用 Vars，
	 * 并强制将它们复制到最上层目标关系的 reltarget 列表中。
	 * 这就足够了，因为它们将在稍后的附加关系扩展中（经过适当转换）复制到各个叶目标关系中 —— 请参见 set_append_rel_size()。
	 */
	fc_target_rel = find_base_rel(fc_root, fc_result_relation);

	foreach(fc_lc, fc_root->processed_tlist)
	{
		TargetEntry *fc_tle = lfirst(fc_lc);
		Var		   *fc_var = (Var *) fc_tle->expr;

		if (fc_var && IsA(fc_var, Var) && fc_var->varno == ROWID_VAR)
		{
			fc_target_rel->reltarget->exprs =
				lappend(fc_target_rel->reltarget->exprs, copyObject(fc_var));
			/* reltarget 成本和宽度将在稍后计算 */
		}
	}
}
